#pragma semicolon 1

#include <sourcemod>
#include <cstrike>
#include <sdkhooks>
#include <sdktools>
#include <WeaponAPI>

#define EF_NODRAW 32

//-----------------------------------------
new bool:SpawnCheck[MAXPLAYERS+1];
new ClientVM[MAXPLAYERS+1][2];
new bool:IsCustom[MAXPLAYERS+1];
//-----------------------------------------

public Plugin:myinfo=
{
	name="Weapon Interface",
	author="Skydive",
	description="Changes Weapon Skins and such. Credit to: Bacardi",
	version="1.0",
	url=""
};

public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
{
	CreateNative("WX_RegisterWeapon", Native_RegisterWeapon);
	CreateNative("WX_GetWeaponReplaceClass", Native_GetWeaponReplaceClass);
	CreateNative("WX_GetWeaponName", Native_GetWeaponName);
	CreateNative("WX_SetWeaponViewModel", Native_SetWeaponViewModel);
	CreateNative("WX_SetWeaponWorldModel", Native_SetWeaponWorldModel);
	CreateNative("WX_RegisteredWeapons", Native_RegisteredWeapons);
	CreateNative("WX_GetWeaponViewModel", Native_GetWeaponViewModel);
	CreateNative("WX_GetWeaponWorldModel", Native_GetWeaponWorldModel);
	
	CreateNative("WX_CallForward", Native_CallForward);
	CreateNative("WX_CallForward3", Native_CallForward3);
	return APLRes_Success;
}

public OnPluginStart()
{
	HookEvent("player_death",Event_PlayerDeath);
	HookEvent("player_hurt",Event_PlayerHurt);
	HookEvent("player_spawn",Event_PlayerSpawn);
	HookEvent("weapon_fire",Event_WeaponFire);
	
	//-------------------------------------------------------------------
	for (new client = 1; client <= MaxClients; client++) 
	{ 
		if (IsClientInGame(client)) 
		{
			SDKHook(client, SDKHook_PostThinkPost, OnPostThinkPost);
			
			//find both of the clients viewmodels
			ClientVM[client][0] = GetEntPropEnt(client, Prop_Send, "m_hViewModel");
			
			new PVM = -1;
			while ((PVM = FindEntityByClassname(PVM, "predicted_viewmodel")) != -1)
			{
				if (GetEntPropEnt(PVM, Prop_Send, "m_hOwner") == client)
				{
					if (GetEntProp(PVM, Prop_Send, "m_nViewModelIndex") == 1)
					{
						ClientVM[client][1] = PVM;
						break;
					}
				}
			}
		}
	}
	//-------------------------------------------------------------------
	
	StartWeaponForwards();
}

public Event_WeaponFire(Handle:event, const String:name[], bool:dontBroadcast)
{
	new Client = GetClientOfUserId(GetEventInt(event, "userid"));
	decl String:ClassName[32];
	
	new WeaponIndex = GetEntPropEnt(Client, Prop_Send, "m_hActiveWeapon");
	if(IsValidEdict(WeaponIndex))
	{
		new WeaponMax = WX_RegisteredWeapons();
		decl String:LoopClassName[32];
		
		GetEdictClassname(WeaponIndex, ClassName, sizeof(ClassName));
			
		for(new i = 0; i<WeaponMax; i++)
		{	
			WX_GetWeaponReplaceClass(i,LoopClassName);
			if (StrEqual(LoopClassName, ClassName, false))
			{
				WX_CallForward(2,Client,i);
			}
		}
	}
	
}





//-------------------------------------------------------------------
public OnClientPutInServer(client)
{
	
	SDKHook(client, SDKHook_PostThinkPost, OnPostThinkPost);
	//WorldModel
	SDKHook(client, SDKHook_WeaponSwitch, WeaponHook);
	SDKHook(client, SDKHook_WeaponEquip, WeaponHook);
}

public OnEntityCreated(entity, const String:classname[])
{
	if (StrEqual(classname, "predicted_viewmodel", false))
	{
		SDKHook(entity, SDKHook_Spawn, OnEntitySpawned);
	}
}

//find both of the clients viewmodels
public OnEntitySpawned(entity)
{
	new Owner = GetEntPropEnt(entity, Prop_Send, "m_hOwner");
	if ((Owner > 0) && (Owner <= MaxClients))
	{
		if (GetEntProp(entity, Prop_Send, "m_nViewModelIndex") == 0)
		{
			ClientVM[Owner][0] = entity;
		}
		else if (GetEntProp(entity, Prop_Send, "m_nViewModelIndex") == 1)
		{
			ClientVM[Owner][1] = entity;
		}
	}
}


public OnPostThinkPost(client)
{
	static OldWeapon[MAXPLAYERS + 1];
	static OldSequence[MAXPLAYERS + 1];
	static Float:OldCycle[MAXPLAYERS + 1];
	
	new WeaponIndex;
	decl String:ClassName[32];
	
	//handle spectators
	if (!IsPlayerAlive(client))
	{
		new spec = GetEntPropEnt(client, Prop_Send, "m_hObserverTarget");
		if (spec != -1)
		{
			WeaponIndex = GetEntPropEnt(spec, Prop_Send, "m_hActiveWeapon");
			if(IsValidEdict(WeaponIndex))
			{
				new WeaponMax = WX_RegisteredWeapons();
				decl String:LoopClassName[32];
				
				GetEdictClassname(WeaponIndex, ClassName, sizeof(ClassName));
				
				for(new i = 0; i<WeaponMax; i++)
				{	
					WX_GetWeaponReplaceClass(i,LoopClassName);
					if (StrEqual(LoopClassName, ClassName, false))
					{
						new WeaponModel = WX_GetWeaponViewModel(i);
						if(WeaponModel != -1)
						{
							SetEntProp(ClientVM[client][1], Prop_Send, "m_nModelIndex", WeaponModel);
							return;
						}
					}
				}
			}

		}
		return;
	}
	
	WeaponIndex = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon");
	new Sequence = GetEntProp(ClientVM[client][0], Prop_Send, "m_nSequence");
	new Float:Cycle = GetEntPropFloat(ClientVM[client][0], Prop_Data, "m_flCycle");
	
	if (WeaponIndex <= 0)
	{
		new EntEffects = GetEntProp(ClientVM[client][1], Prop_Send, "m_fEffects");
		EntEffects |= EF_NODRAW;
		SetEntProp(ClientVM[client][1], Prop_Send, "m_fEffects", EntEffects);
		
		IsCustom[client] = false;
			
		OldWeapon[client] = WeaponIndex;
		OldSequence[client] = Sequence;
		OldCycle[client] = Cycle;
		
		return;
	}
	
	//just stuck the weapon switching in here aswell instead of a separate hook
	if (WeaponIndex != OldWeapon[client])
	{
		if(IsValidEdict(WeaponIndex))
		{
			new bool:Customized = false;
			new WeaponMax = WX_RegisteredWeapons();
			decl String:LoopClassName[32];
			
			GetEdictClassname(WeaponIndex, ClassName, sizeof(ClassName));
			
			for(new i = 0; i<WeaponMax; i++)
			{
				WX_GetWeaponReplaceClass(i,LoopClassName);
				if (StrEqual(LoopClassName, ClassName, false))
				{
					new WeaponModel = WX_GetWeaponViewModel(i);
					if(WeaponModel != -1)
					{
						decl String:CustomName[32];
						
						new EntEffects = GetEntProp(ClientVM[client][0], Prop_Send, "m_fEffects");
						EntEffects |= EF_NODRAW;
						SetEntProp(ClientVM[client][0], Prop_Send, "m_fEffects", EntEffects);
						
						EntEffects = GetEntProp(ClientVM[client][1], Prop_Send, "m_fEffects");
						EntEffects &= ~EF_NODRAW;
						SetEntProp(ClientVM[client][1], Prop_Send, "m_fEffects", EntEffects);
						
						SetEntProp(ClientVM[client][1], Prop_Send, "m_nModelIndex", WeaponModel);
						SetEntPropEnt(ClientVM[client][1], Prop_Send, "m_hWeapon", GetEntPropEnt(ClientVM[client][0], Prop_Send, "m_hWeapon"));
						
						SetEntProp(ClientVM[client][1], Prop_Send, "m_nSequence", GetEntProp(ClientVM[client][0], Prop_Send, "m_nSequence"));
						SetEntPropFloat(ClientVM[client][1], Prop_Send, "m_flPlaybackRate", GetEntPropFloat(ClientVM[client][0], Prop_Send, "m_flPlaybackRate"));
						
						IsCustom[client] = true;
						Customized=true;
						WX_CallForward(0,client,i);
						WX_GetWeaponName(i,CustomName);
						if(!StrEqual(CustomName,""))
						{
							PrintHintText(client,"%s",CustomName);
						}
					}
				}
			}
			if(Customized == false)
			{
						
				//hide unused viewmodel if the current weapon isn't using it
				new EntEffects = GetEntProp(ClientVM[client][1], Prop_Send, "m_fEffects");
				EntEffects |= EF_NODRAW;
				SetEntProp(ClientVM[client][1], Prop_Send, "m_fEffects", EntEffects);
				IsCustom[client] = false;

				WeaponMax = WX_RegisteredWeapons();
				if(IsValidEdict(OldWeapon[client]))
				{
					GetEdictClassname(OldWeapon[client], ClassName, sizeof(ClassName));
					
					for(new i = 0; i<WeaponMax; i++)
					{
						WX_GetWeaponReplaceClass(i,LoopClassName);
						if (StrEqual(LoopClassName, ClassName, false))
						{
							WX_CallForward(1,client,i);
						}
					}
				}
			}
		}	
	}
	else
	{
		if (IsCustom[client])
		{
			//copy the animation stuff from the viewmodel to the used one every frame
			SetEntProp(ClientVM[client][1], Prop_Send, "m_nSequence", GetEntProp(ClientVM[client][0], Prop_Send, "m_nSequence"));
			SetEntPropFloat(ClientVM[client][1], Prop_Send, "m_flPlaybackRate", GetEntPropFloat(ClientVM[client][0], Prop_Send, "m_flPlaybackRate"));
			
			if ((Cycle < OldCycle[client]) && (Sequence == OldSequence[client]))
			{
				SetEntProp(ClientVM[client][1], Prop_Send, "m_nSequence", 0);
			}
		}
	}
	//hide viewmodel a frame after spawning
	if (SpawnCheck[client])
	{
		SpawnCheck[client] = false;
		if (IsCustom[client])
		{
			new EntEffects = GetEntProp(ClientVM[client][0], Prop_Send, "m_fEffects");
			EntEffects |= EF_NODRAW;
			SetEntProp(ClientVM[client][0], Prop_Send, "m_fEffects", EntEffects);
		}
	}
	
	OldWeapon[client] = WeaponIndex;
	OldSequence[client] = Sequence;
	OldCycle[client] = Cycle;
}
//hide viewmodel on death
public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
	new UserId = GetEventInt(event, "userid");
	new client = GetClientOfUserId(UserId);
	
	new EntEffects = GetEntProp(ClientVM[client][1], Prop_Send, "m_fEffects");
	EntEffects |= EF_NODRAW;
	SetEntProp(ClientVM[client][1], Prop_Send, "m_fEffects", EntEffects);
	
	new attacker = GetClientOfUserId(GetEventInt(event,"attacker"));
	new WeaponIndex = GetEntPropEnt(attacker, Prop_Send, "m_hActiveWeapon");
	new WeaponMax = WX_RegisteredWeapons();
	decl String:ClassName[32];
	decl String:LoopClassName[32];
			
	GetEdictClassname(WeaponIndex, ClassName, sizeof(ClassName));
			
	for(new i = 0; i<WeaponMax; i++)
	{
		WX_GetWeaponReplaceClass(i,LoopClassName);
		if (StrEqual(LoopClassName, ClassName, false))
		{
			WX_CallForward3(4,attacker,i,client);
		}
	}
}
public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast)
{
	new client = GetClientOfUserId(GetEventInt(event, "userid"));
	new attacker = GetClientOfUserId(GetEventInt(event,"attacker"));
	new WeaponIndex = GetEntPropEnt(attacker, Prop_Send, "m_hActiveWeapon");
	new WeaponMax = WX_RegisteredWeapons();
	decl String:ClassName[32];
	decl String:LoopClassName[32];
			
	GetEdictClassname(WeaponIndex, ClassName, sizeof(ClassName));
			
	for(new i = 0; i<WeaponMax; i++)
	{
		WX_GetWeaponReplaceClass(i,LoopClassName);
		if (StrEqual(LoopClassName, ClassName, false))
		{
			WX_CallForward3(3,attacker,i,client);
		}
	}
}
//when a player repsawns at round start after surviving previous round the viewmodel is unhidden
public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{
	new UserId = GetEventInt(event, "userid");
	new client = GetClientOfUserId(UserId);
	
	//use to delay hiding viewmodel a frame or it won't work
	SpawnCheck[client] = true;
	
	// WorldModel
	new ActiveWeapon = GetEntPropEnt(client, Prop_Data, "m_hActiveWeapon");
	if(IsValidEdict(ActiveWeapon))
	{
		CreateTimer(0.1, ChangeWorldModel, client);
	}
}  

public Action:WeaponHook(Client, weapon)
{
	new ActiveWeapon = GetEntPropEnt(Client, Prop_Data, "m_hActiveWeapon");
	if(IsValidEdict(ActiveWeapon))
	{
		CreateTimer(0.1, ChangeWorldModel, Client);
	}
	return Plugin_Continue;
}

// WorldModel
public Action:ChangeWorldModel(Handle:Timer, any:Client)
{
	new WeaponIndex = GetEntPropEnt(Client, Prop_Send, "m_hActiveWeapon");
	
	if(IsValidEdict(WeaponIndex))
	{
		new WeaponMax = WX_RegisteredWeapons();
		decl String:ClassName[32];
		decl String:LoopClassName[32];
	
		GetEdictClassname(WeaponIndex, ClassName, sizeof(ClassName));
		for(new i = 0; i<WeaponMax; i++)
		{	
			WX_GetWeaponReplaceClass(i,LoopClassName);
			if (StrEqual(LoopClassName, ClassName, false))
			{
				new WeaponModel = WX_GetWeaponWorldModel(i);
				if(WeaponModel != -1)
				{
					SetEntProp(WeaponIndex, Prop_Send, "m_iWorldModelIndex", WeaponModel);
				}
			}
		}
	}
}